package com.tzstcl.framework.aspect;

import com.tzstcl.framework.exception.RepeatSubmitException;
import com.tzstcl.framework.redis.RedisLockService;
import com.tzstcl.framework.shiro.ShiroUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.UUID;

@Aspect
@Component
public class RepeatSubmitAspect {

    private static final Logger logger = LoggerFactory.getLogger(RepeatSubmitAspect.class);

    @Autowired
    private RedisLockService redisLockService;

    @Pointcut("@annotation(noRepeatSubmit)")
    public void pointCut(NoRepeatSubmit noRepeatSubmit) {
    }

    @Around("pointCut(noRepeatSubmit)")
    public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) throws Throwable {
        //获取请求锁锁定时间
        int lockTime = noRepeatSubmit.lockTime();
        //取uri
        ServletRequestAttributes sra = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();
        Assert.notNull(request, "request can not null");
        String path = request.getServletPath();
        //取用户ID
        Long userId = ShiroUtils.getUserId();
        if(userId == null){
            throw new RepeatSubmitException("用户未登录!");
        }
        //拼接防重复请求锁的key和value
        String lockKey = userId.toString() + path;
        String lockValue = UUID.randomUUID().toString();

        //上锁
        boolean enLockSuccess = redisLockService.enLock(lockKey, lockValue, lockTime);
        if (!enLockSuccess) {
            logger.debug("获取防重复请求锁失败: key = {}", lockKey);
            throw new RepeatSubmitException("请求过于频繁!");
        } else {
            logger.debug("获取防重复请求锁成功: key = {}", lockKey);
            //业务代码执行结果
            Object result;
            try {
                //执行进程(业务代码)
                result = pjp.proceed();
                return result;
            } catch (Exception e){
                //解锁
                logger.error("捕获到运行时异常, 开始解除防重复请求锁: key = {}", lockKey);
                boolean deLockSuccess = redisLockService.deLock(lockKey, lockValue);
                throw e;
            }
        }

    }
}
